<?php
/**
 * PhpStorm
 * User: zucheng
 * Date: 2019/3/28
 */

namespace App\Services;

use App\Models\MsgFee;
use App\Models\PayRepeat;
use App\Traits\PayHelper;
use Illuminate\Support\Facades\Log;
use Symfony\Component\HttpFoundation\Response;

class PayService extends BaseService
{
    use PayHelper;

    /**
     * 支付结果查询
     * alipay doc: https://docs.open.alipay.com/api_1/alipay.trade.query/
     * wechat doc: https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_2
     * @param $tradeNo 商户订单号
     * @param int $payment 支付方式 1支付宝 2微信
     * @return bool true已支付 false未支付
     * @throws \Exception
     */
    public function findPayStatus($tradeNo, int $payment)
    {
        try {
            switch ($payment) {
                case MsgFee::PAYMENT_ALI:
                    $ret = $this->alipayOrderFind($tradeNo);
                    if ($ret->code == 10000 && in_array($ret->trade_status, ['TRADE_SUCCESS', 'TRADE_FINISHED'])) {
                        return true;
                    }
                    throw new \Exception("未支付,code:{$ret->code},trade_status:{$ret->trade_status}");
                case MsgFee::PAYMENT_WECHAT:
                    $ret = $this->wechatOrderFind($tradeNo);
                    // TODO 待确定返回参数格式
                    //$ret = $ret->toJson();
                    if (isset($ret->trade_state) && in_array($ret->trade_state, ['SUCCESS', 'REFUND'])) {
                        return true;
                    }
                    throw new \Exception("{未支付,trade_state:{$ret->trade_state}");
                default:
                    return false;
            }

            return false;
        } catch (\Exception $e) {
            Log::notice('未查询到订单：' . $e->getMessage());
            return false;
        }
    }

    /**
     * app支付 [获取支付所需参数]
     * alipay doc: https://docs.open.alipay.com/204/105465/
     * wechat doc: https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_1
     * @param $payment 支付方式 1支付宝 2微信
     * @param $tradeNo 商户订单号
     * @param $amount 金额，单位：分
     * @param $subject 支付备注
     * @return array|string|bool
     * @throws \Exception
     */
    public function payByApp($payment, $tradeNo, $amount, $subject)
    {
        switch ($payment) {
            case MsgFee::PAYMENT_ALI:
                // 支付返回字符串
                $ret = $this->alipayByApp($tradeNo, $amount, $subject);
                if ($ret->getStatusCode() != Response::HTTP_OK) {
                    throw new \Exception('生成支付宝请求参数失败');
                }
                return $ret->getContent();
            case MsgFee::PAYMENT_WECHAT:
                // 返回支付所需数据
                $ret = $this->wechatPayByApp($tradeNo, $amount, $subject);
                if ($ret->getStatusCode() != Response::HTTP_OK) {
                    throw new \Exception('生成微信支付请求参数失败');
                }
                return $ret->getContent();
            default:
                return false;
        }

        return false;
    }

    /**
     * 小程序支付 [获取支付所需参数]
     * alipay doc: https://docs.open.alipay.com/204/105465/
     * wechat doc: https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_1
     * @param $payment 支付方式 1支付宝 2微信
     * @param $tradeNo 商户订单号
     * @param $amount 金额，单位：分
     * @param $subject 支付备注
     * @param $openid 用户表示
     * @return array|bool
     * @throws \Exception
     */
    public function payByMiniapp($payment, $tradeNo, $amount, $subject, $openid)
    {
        switch ($payment) {
            case MsgFee::PAYMENT_ALI:
                throw new \Exception('暂不支持支付宝小程序支付');
            case MsgFee::PAYMENT_WECHAT:
                // 返回支付所需数据
                $ret = $this->wechatPayByMiniapp($tradeNo, $amount, $subject, $openid);
                return $ret->toArray();
            default:
                return false;
        }

        return false;
    }

    /**
     * 支付宝支付结果异步通知处理
     * doc: https://docs.open.alipay.com/204/105301/
     * @param $data 支付宝通知验证后的数据
     * @return bool
     * @throws \Exception
     */
    public function alipayCallbackDo($data)
    {
        /*
         * 1.异步通知验证
         * 自行对 trade_status 进行判断，支付宝的通知中，状态为 TRADE_SUCCESS 或 TRADE_FINISHED 时，才会认定为付款成功
         * 订单号：$data->out_trade_no
         * 订单总金额：$data->total_amount
         * 业务唯一标识：$data->app_id
         */
        // 通知验证
        if (!in_array($data->trade_status, ['TRADE_SUCCESS', 'TRADE_FINISHED'])) {
            throw new \Exception("[支付宝回调通知]支付失败，状态说明：{$data->trade_status}，商户订单号：{$data->out_trade_no}");
        }
        if ($data->app_id != config('pay.alipay.app_id')) {
            throw new \Exception("[支付宝回调通知]未知业务，错误业务ID：{$data->app_id}，商户订单号：{$data->out_trade_no}");
        }

        // 订单号和金额验证
        $msgFee = MsgFee::where('out_trade_no', $data->out_trade_no)->firstOrFail();
        if (($data->total_amount * 100) != $msgFee->total_fee) {
            throw new \Exception("[支付宝回调通知]金额不匹配，商户订单号：{$data->out_trade_no}, 实际金额：{$msgFee->total_fee} 分，通知金额：{$data->total_amount} 元");
        }

        // 订单状态和支付方式验证
        if ($msgFee->pay_status >= MsgFee::PAYED) {

            // 可能的异常：先调用支付宝未支付，又调用了微信支付, 最后使用支付宝再次重复支付。处理办法：以先通知的为主，后通知的需要退款
            if ($msgFee->payment != MsgFee::PAYMENT_ALI) {
                // 需要退款的记录
                $repeat = PayRepeat::where('out_trade_no', $data->out_trade_no)->first();
                if (!$repeat) {
                    PayRepeat::create([
                        'driver_uuid'  => $msgFee->driver_uuid,
                        'payment'      => MsgFee::PAYMENT_ALI,
                        'out_trade_no' => $msgFee->out_trade_no,
                        'total_fee'    => $msgFee->total_fee,
                        'subject'      => $msgFee->subject,
                    ]);
                }

                throw new \Exception("[支付宝回调通知]重复支付，商户订单号：{$data->out_trade_no}");
            }

            throw new \Exception("[支付宝回调通知]重复通知，商户订单号：{$data->out_trade_no}");
        }

        // 支付方式验证
        if ($msgFee->payment != MsgFee::PAYMENT_ALI) {
            $msgFee->payment = MsgFee::PAYMENT_ALI;
        }

        // 2.修改支付状态
        $msgFee->pay_status = MsgFee::PAYED;
        $msgFee->save();

        // 生成shipping记录
        $shippingData = [
            'driver_uuid' => $msgFee->driver_uuid,
            'order_id'    => $msgFee->order_id,
            'msg_fee_id'  => $msgFee->id
        ];
        $createRet    = ShippingService::getInstance()->createShipping($shippingData);
        if ($createRet['code'] != 0) {
            throw new \Exception("[支付宝回调通知]生成货运记录失败，商户订单号：{$data->out_trade_no}");
        }

        return true;
    }

    /**
     * 微信支付结果异步通知处理
     * doc: https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_7&index=3
     * @param $data 微信通知验证后的数据
     * @return bool
     * @throws \Exception
     */
    public function wechatPayCallbackDo($data)
    {
        /*
         * 1.异步通知验证
         * 自行对 result_code 进行判断，SUCCESS 认定为付款成功
         * 订单号：$data->out_trade_no
         * 订单总金额：$data->total_fee
         * 业务唯一标识：$data->appid
         */
        // 通知验证
        if ($data->result_code != 'SUCCESS') {
            throw new \Exception("[微信回调通知]支付失败，状态说明：{$data->result_code}，商户订单号：{$data->out_trade_no}，错误代码：{$data->err_code}，错误代码描述：{$data->err_code_des}");
        }
        if ($data->appid != config('pay.wechat.appid') || $data->mch_id != config('pay.wechat.mch_id')) {
            throw new \Exception("[微信回调通知]未知业务，错误业务ID：{$data->appid}，商户号：{$data->mch_id}，商户订单号：{$data->out_trade_no}");
        }

        // 订单号和金额验证
        $msgFee = MsgFee::where('out_trade_no', $data->out_trade_no)->firstOrFail();
        if ($data->total_fee != $msgFee->total_fee) {
            throw new \Exception("[微信回调通知]金额不匹配，商户订单号：{$data->out_trade_no}, 实际金额：{$msgFee->total_fee} 分，通知金额：{$data->total_fee} 分");
        }

        // 订单状态和支付方式验证
        if ($msgFee->pay_status >= MsgFee::PAYED) {

            // 可能的异常：先调用微信未支付，又调用了支付宝支付成功, 最后使用微信再次重复支付。处理办法：以先通知的为主，后通知的需要退款
            if ($msgFee->payment != MsgFee::PAYMENT_WECHAT) {
                // 需要退款的记录
                $repeat = PayRepeat::where('out_trade_no', $data->out_trade_no)->first();
                if (!$repeat) {
                    PayRepeat::create([
                        'driver_uuid'  => $msgFee->driver_uuid,
                        'payment'      => MsgFee::PAYMENT_WECHAT,
                        'out_trade_no' => $msgFee->out_trade_no,
                        'total_fee'    => $msgFee->total_fee,
                        'subject'      => $msgFee->subject,
                    ]);
                }

                throw new \Exception("[微信回调通知]重复支付，商户订单号：{$data->out_trade_no}");
            }

            throw new \Exception("[微信回调通知]重复通知，商户订单号：{$data->out_trade_no}");
        }

        // 支付方式验证
        if ($msgFee->payment != MsgFee::PAYMENT_WECHAT) {
            $msgFee->payment = MsgFee::PAYMENT_WECHAT;
        }

        // 2.修改支付状态
        $msgFee->pay_status = MsgFee::PAYED;
        $msgFee->save();

        // 生成shipping记录
        $shippingData = [
            'driver_uuid' => $msgFee->driver_uuid,
            'order_id'    => $msgFee->order_id,
            'msg_fee_id'  => $msgFee->id
        ];
        $createRet    = ShippingService::getInstance()->createShipping($shippingData);
        if ($createRet['code'] != 0) {
            throw new \Exception("[微信回调通知]生成货运记录失败，商户订单号：{$data->out_trade_no}");
        }

        return true;
    }

}